/*
 * FastaRecordSource.java
 *
 * Created on September 26, 2006, 1:11 PM
 * Brought forward from bugs2
 */

package biofilter.sources;

import biofilter.exceptions.InvalidRecordException;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 * To handle the multi-line input of a FastaRecords  
 */
public class FastaRecordSource extends RecordsFile{
    private String qualityFilename = null;
    private BufferedReader qHandle;
    
    /** Creates a new instance of FastaRecordSource */
    public FastaRecordSource(Object source) throws InvalidRecordException   {
        super( source, "biofilter.records.FastaRecord");
    }
    
    public FastaRecordSource(Object source, Object qualitySource) throws InvalidRecordException   {
        super( source, "biofilter.records.FastaRecord");
        setQualitySource(qualitySource);
    }
    
    private void setQualitySource(Object qualitySource) throws InvalidRecordException {        
        if(qualitySource instanceof String) {
            this.qualityFilename = (String) qualitySource; 
            try {
                qHandle = new BufferedReader(new FileReader(this.qualityFilename));
            } catch (IOException ioe) {
                throw new InvalidRecordException("Error in setQualitySource: " + ioe.getMessage());
            }
        } else {
            throw new InvalidRecordException("Wrong data type for qualitySource, " +
                    "expected String type, but found " + qualitySource.getClass().getName(), true);
        }
        
        if(!validate(this.filename, this.qualityFilename)) {
            throw new InvalidRecordException("The quality file does NOT match the fasta file.", true);
        }
    }
    
    @Override
    protected String readRecordString() throws InvalidRecordException{
        String oldRecordString = this.currentRecordString;
        this.currentRecordString = null;
	StringBuffer currentRecordBuffer = null; 
        StringBuffer qualityBuffer = null;
        boolean firstRead = true;
        char firstChar;
        
        try {
            //get fasta record
            while( true){
                this.handle.mark(5);
                int intVal = handle.read();
                this.handle.reset();
                firstChar = (char) intVal;
                
                if ( intVal == -1 || (!firstRead && (firstChar == '>'))) {
                    break;
                } else {
                    String line = handle.readLine().trim();
                    if ( firstRead){
			currentRecordBuffer = new StringBuffer(line+"\n");
                        firstRead = false;
                    } else {
                        if ( line.equals( "")){
                            continue;
                        } else {
                            currentRecordBuffer.append(line+"\n");	
                        }
                    }
                }
            }
            
           //get quality values to append to oldRecordString
            if(this.qualityFilename != null) {
                firstRead = true;
                while( true){
                    this.qHandle.mark(5);
                    int intVal = qHandle.read();
                    this.qHandle.reset();
                    firstChar = (char) intVal;

                    if ( intVal == -1 || (!firstRead && (firstChar == '>'))) {
                        break;
                    } else {
                        String line = qHandle.readLine().trim();
                        if ( firstRead){
                            qualityBuffer = new StringBuffer(line+"\n");
                            firstRead = false;
                        } else {
                            if ( line.equals( "")){
                                continue;
                            } else {
                                qualityBuffer.append(line+"\n");	
                            }
                        }
                    }
                }
            }
            
            //append quality to oldRecordString
            if(qualityBuffer != null) {
                oldRecordString += qualityBuffer.toString();
            }
            
            if (currentRecordBuffer == null) {
                eof = true;
            }else{
		currentRecordString = currentRecordBuffer.toString(); 
	    }
            return oldRecordString;
            
        } catch(IOException e) {
            throw new InvalidRecordException( e.getMessage(), false);
        }
    }
    
    private boolean validate(String fastaFile, String qualityFile) throws InvalidRecordException {
        try {            
            BufferedReader fastaHandle = new BufferedReader(new FileReader(fastaFile));
            BufferedReader qualityHandle = new BufferedReader(new FileReader(qualityFile));
            int fVal;
            int qVal;
            String fLine = null;
            String qLine = null;
            String fHeader = null;
            String qHeader = null;
            StringBuffer fastaSeq = null;
            StringBuffer qualitySeq = null;
            int fCount;
            int qCount;
            int lineNumber = 0;
            
            while( true){ 
                fastaHandle.mark(5);
                qualityHandle.mark(5);
                fVal = fastaHandle.read();
                qVal = qualityHandle.read();
                fastaHandle.reset();
                qualityHandle.reset();
                
                lineNumber++;
                String lineNumMsg = "Error at Line " + lineNumber + ": ";
                
                if(fVal == -1 && qVal == -1) {
                    break;
                }
                               
                fHeader = fLine;
                qHeader = qLine;
                fastaSeq = new StringBuffer();
                qualitySeq = new StringBuffer();
                
                while((fLine = fastaHandle.readLine()) != null) {                 
                    lineNumber++;
                    fLine = fLine.trim();
                    if(fLine.indexOf(">") != -1) {
                        break;
                    }
                    if(!fLine.equals("")) {//fasta content
                        fastaSeq.append(fLine); 
                    }
                } 
                
                while((qLine = qualityHandle.readLine()) != null) {
                    qLine.trim();
                    if(qLine.indexOf(">") != -1) {
                        break;
                    }
                    if(!qLine.equals("")) {//quality content
                        qualitySeq.append(qLine + " ");
                    }
                } 
                
                if(fastaSeq.toString().length() ==0 && qualitySeq.toString().length() == 0) {
                    continue;//get the first sequences
                }
                
                //Check whether the fasta sequence header matches the quality sequence header
                if(!fHeader.equals(qHeader)) {
                    Logger.getLogger(this.getClass().getName()).log(Level.WARNING,
                            "quality header '" + qHeader + "' does not match sequence header '" + fHeader + "'.");
                    return false;
                }
                
                //Check whether the size of fasta bases matches the size of quality values
                fCount = fastaSeq.toString().length();
                qCount = qualitySeq.toString().trim().split("\\s+").length;

                if(fCount != qCount) {
                    Logger.getLogger(this.getClass().getName()).log(Level.SEVERE,
                            "quality data size does not match sequence size.");
                    return false;
                }
            }
        } catch (IOException ioe) {
            throw new InvalidRecordException("Error in validating fasta, quality files: " + ioe.getMessage());
        } 
        
        return true;
    }
}

